#include "Voleur.h"

#include <iomanip>

#include "lib.h"

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

/**
 * Contient les valeurs de base des talents de voleur. Un '100' signifie que
 * La compétence n'est pas associée à un pourcentage.
 */
const vector<unsigned> Voleur::comp_base = list_of
  (100)(5)(10)(10)(15)(5)(10)(100)(10)(60)(100)
  (0)(15)(5)(100)(100)(5)(15)(100)(100)(100);

const unsigned Voleur::nbrCompetences = 21;

const unsigned Voleur::nbrRestrictions = 0;

const unsigned Voleur::ppAlloues = 80;

const vector<CompetenceClasse> Voleur::Comp = list_of
  (CompetenceClasse("Attaque sournoise"                 , 10, Basique))
  (CompetenceClasse("Corruption"                        ,  5, true ,  5))
  (CompetenceClasse("Crocheter les serrures"            , 10, true , 10))
  (CompetenceClasse("Déplacement silencieux"            ,  5, true , 10))
  (CompetenceClasse("Détection des bruits"              ,  5, true , 15))
  (CompetenceClasse("Détection de la magie"             , 10, true ,  5))
  (CompetenceClasse("Détection des illusions"           , 10, true , 10))
  (CompetenceClasse("Esquive"                           , 10))
  (CompetenceClasse("Evasion"                           , 10, true , 10))
  (CompetenceClasse("Grimper"                           ,  5, true , 60))
  (CompetenceClasse("Jargon des voleurs"                ,  5))
  (CompetenceClasse("Lire des langues inconnues"        ,  5, true ,  0))
  (CompetenceClasse("Percer un tunnel"                  , 10, true , 15))
  (CompetenceClasse("Se cacher dans l'ombre"            ,  5, true ,  5))
  (CompetenceClasse("Suivant (niveau 10)"               ,  5))
  (CompetenceClasse("Suivants"                          , 10))
  (CompetenceClasse("Trouver et désamorcer les pièges"  , 10, true ,  5))
  (CompetenceClasse("Vol à la tire"                     , 10, true , 15))
  (CompetenceClasse("Utilisation de parchemins (niv 10)",  5, Pourcentage))
  (CompetenceClasse("Utilisation de parchemins"         , 10, Pourcentage))
  (CompetenceClasse("Specialisation"                    , 15));

const vector< Prerequis > Voleur::Limites = list_of
  (Prerequis(1, 9));

const set< string > Voleur::RacesAdmises = list_of
  ( "Humain" ) ( "Demi-Elfe" ) ( "Demi-Ogre" ) ( "Demi-Orque" )
  ( "Elfe" ) ( "Gnome" ) ( "Hobbit" ) ( "Nain" );

/** Constructeur usuel.
 * Permet la construction d'un voleur de A à Z, càd on détermine ici
 * les restrictions, puis les compétences.
 *
 * @param[in] xp le nombre de pts d'expérience du Voleur.
 * @param[in] trait les traits du Voleur, le 7ème contient le score de
 * force exceptionnelel, fixé à 0 si non applicable.
 * @param[in] PP nombre de points de personnage (PP) à disposition
 * pour la création du Voleur.
 * @param[in] race nom de la race du personnage.
 */
Voleur::Voleur(unsigned xp, const vector< unsigned >& trait,
	       unsigned& PP, const string& race)
  : Classe( xp, Roublards, "Voleur" )
{
  /*Debug::*/empileNiveau( &elementaire );

  //NomClasse = "Voleur";
  NombrePPsAlloues = ppAlloues;
  PP += NombrePPsAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  blank() << setfill('*') << setw(56) << '*' << endmsg;
  blank() << "Création d'un voleur" << endmsg << endmsg;

  construitClasse( PP, trait, race );

  blank() << endmsg << setfill('*') << setw(56) << '*' << endmsg;
  calculNiveau();

  /*Debug::*/depileNiveau();//dout.setLevel(etat_precedent);
}

/** Constructeur simplifié.
 * Permet de construire un Voleur lors de la lecture d'un ficier xml.
 *
 * @param[in] xp le nombre de pts d'expérience du Voleur.
 * @param[in] trait les traits du Voleur, le 7ème contient le score de
 * force exceptionnelle, fixé à 0 si non applicable.
 * @param[in] race nom de la race du personnage.
 */
Voleur::Voleur( unsigned xp )
  : Classe( xp, Roublards, "Voleur" )
{
  //NomClasse = "Voleur";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
}

/** Constructeur pour le mode graphique.
 * Permet de construire une instance de Mage avec le contenu
 * minimal, le nombre de points d'expériences et les capacités étant
 * déterminées après la déclaration.
 *
 * @param[in] trait les traits du Mage, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] UI contrôle, est présent pour des raisons historiques.
 * @param[in] race nom de la race du personnage.
 */
Voleur::Voleur( const bool& UI )
  : Classe( 0, Roublards, "Voleur" )
{
  if (!UI)
    {
      error() << "Création du personnage impossible" << endmsg;
      exit(1);
    }

  //NomClasse = "Voleur";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
}

/**
 * Constructeur de "copie", destiné à être utilisé depuis MetaClasse.
 *
 * @param[in] ptr pointeur sur un objet de type Classe (MetaClasse en
 * fait).
 */
Voleur::Voleur(Classe *ptr)
  : Classe( *ptr )
{
  NomClasse = "Voleur";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
}

/**
 * Destructeur, ne fait rien de spécial
 */
Voleur::~Voleur()
{}

void Voleur::calculNiveau()
{
  if (XP < 75000)
    {
      unsigned i;
      for (i = 0; i < 5; i++)
	{
	  if (XP < (unsigned) 1250 * puissance(2, i))
	    break;
	}
      Niveau = i + 1;
    }
  else if (XP < 70000)
    Niveau = 7;
  else if (XP < 110000)
    Niveau = 8;
  else if (XP < 160000)
    Niveau = 9;
  else if (XP < 220000)
    Niveau = 10;
  else
      Niveau = (XP - 220000) / 220000 + 11;
}

// /**
//  * Ici il faut faire attention aux pourcentages.
//  */
// void Voleur::afficher_stats(ostream& os)
// {
//   os << "Voleur" << endmsg;
//   for (unsigned i(0); i<6; i++)
//     os << Caracteristiques[i] << ' ' << Traits[i] << endmsg;
//   for (unsigned i(0); i<Competences.size(); i++)
//     {
//       if(Comp[Competences[i]].Pourcentage)
// 	{
// 	  os << Comp[Competences[i]].Comp << ' ' << Competences[i+1];
// 	  os << '%' << endmsg;
// 	  i++;
// 	}
//       else
// 	os << Comp[Competences[i]].Comp << endmsg;
//     }
// }

/**
 * Ici sont appliqués les modifications aux talents de voleur dues à
 * la Race et à la Dextérité.
 */
void Voleur::appliquerModif( const string& nomRace, const unsigned& dexterite )
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Voleur::appliquerModif -> début" << endmsg;

  vector< int > *comp_bonus;

  determinerScoresAjustes( nomRace, dexterite, comp_bonus );
  
//   if (type == "Nain")
//     {
//       comp_bonus[1]  -= 5;   // corruption
//       comp_bonus[2]  += 10;  // crochetage
//       comp_bonus[5]  += 5;   // détection de la magie
//       comp_bonus[6]  += 5;   // détection des illusions
//       comp_bonus[9]  -= 10;  // escalade
//       comp_bonus[11] -= 5;   // lire des langues inconnues
//       comp_bonus[12] += 10;  // creuser des tunnels
//       comp_bonus[16] += 15;  // trouver et désamorcer les pièges
//     }
//   else if (type == "Elfe")
//     {
//       comp_bonus[1]  += 15;  // corruption
//       comp_bonus[2]  -= 5;   // crochetage
//       comp_bonus[3]  += 5;   // mouvement silencieux
//       comp_bonus[4]  += 5;   // détection des bruits
//       comp_bonus[5]  += 10;  // détection de la magie
//       comp_bonus[12] -= 10;  // creuser des tunnels
//       comp_bonus[13] += 10;  // se cacher dans l'ombre
//       comp_bonus[17] += 5;   // vol à la tire
//     }
//   else if (type == "Gnome")
//     {
//       comp_bonus[1]  += 5;   // corruption
//       comp_bonus[2]  += 5;   // crochetage
//       comp_bonus[3]  += 5;   // mouvement silencieux
//       comp_bonus[4]  += 10;  // détection des bruits
//       comp_bonus[5]  += 5;   // détection de la magie
//       comp_bonus[6]  += 10;  // détection des illusions
//       comp_bonus[9]  -= 15;  // escalade
//       comp_bonus[12] += 5;   // creuser des tunnels
//       comp_bonus[13] += 5;   // se cacher dans l'ombre
//       comp_bonus[16] += 10;  // trouver et désamorcer les pièges
//     }
//   else if (type == "Demi-Elfe")
//     {
//       comp_bonus[1]  += 5;   // corruption
//       comp_bonus[5]  += 5;   // détection de la magie
//       comp_bonus[6]  += 5;   // détection des illusions
//       comp_bonus[12] -= 5;   // creuser des tunnels
//       comp_bonus[13] += 5;   // se cacher dans l'ombre
//       comp_bonus[17] += 10;  // vol à la tire
//     }
//   else if (type == "Demi-Orque")
//     {
//       comp_bonus[1]  += 10;  // corruption
//       comp_bonus[4]  += 5;   // détection des bruits
//       comp_bonus[6]  -= 5;   // détection des illusions
//       comp_bonus[16] += 5;   // trouver et désamorcer les pièges
//     }
//   else if (type == "Hobbit")
//     {
//       comp_bonus[2]  += 5;   // crochetage
//       comp_bonus[3]  += 10;  // mouvement silencieux
//       comp_bonus[4]  += 5;   // détection des bruits
//       comp_bonus[5]  += 5;   // détection de la magie
//       comp_bonus[8]  += 10;  // évasion
//       comp_bonus[9]  -= 15;  // escalade
//       comp_bonus[11] -= 5;   // lire des langues inconnues
//       comp_bonus[12] += 5;   // creuser des tunnels
//       comp_bonus[13] += 15;  // se cacher dans l'ombre
//       comp_bonus[16] += 5;   // trouver et désamorcer les pièges
//       comp_bonus[17] += 5;   // vol à la tire
//     }

//   switch(Traits[1])
//     {
//     case 9:
//       comp_bonus[2]  -= 10;  // crochetage
//       comp_bonus[3]  -= 20;  // mouvement silencieux
//       comp_bonus[8]  -= 15;  // évasion
//       comp_bonus[9]  -= 10;  // escalade
//       comp_bonus[12] -= 10;  // creuser des tunnels
//       comp_bonus[13] -= 10;  // se cacher dans l'ombre
//       comp_bonus[16] -= 10;  // trouver et désamorcer les pièges
//       comp_bonus[17] -= 15;  // vol à la tire
//       break;
//     case 10:
//       comp_bonus[2]  -= 5;   // crochetage
//       comp_bonus[3]  -= 15;  // mouvement silencieux
//       comp_bonus[8]  -= 10;  // évasion
//       comp_bonus[9]  -= 5;   // escalade
//       comp_bonus[12] -= 5;   // creuser des tunnels
//       comp_bonus[13] -= 5;   // se cacher dans l'ombre
//       comp_bonus[16] -= 10;  // trouver et désamorcer les pièges
//       comp_bonus[17] -= 10;  // vol à la tire
//       break;
//     case 11:
//       comp_bonus[3]  -= 10;  // mouvement silencieux
//       comp_bonus[8]  -= 5;   // évasion
//       comp_bonus[16] -= 5;   // trouver et désamorcer les pièges
//       comp_bonus[17] -= 5;   // vol à la tire
//       break;
//     case 12:
//       comp_bonus[3]  -= 5;   // mouvement silencieux
//       break;
//     case 16:
//       comp_bonus[2]  += 5;   // crochetage
//       break;
//     case 17:
//       comp_bonus[2]  += 10;  // crochetage
//       comp_bonus[3]  += 5;   // mouvement silencieux
//       comp_bonus[8]  += 5;   // évasion
//       comp_bonus[9]  += 5;   // escalade
//       comp_bonus[13] += 5;   // se cacher dans l'ombre
//       comp_bonus[17] += 5;   // vol à la tire
//       break;
//     case 18:
//       comp_bonus[2]  += 15;  // crochetage
//       comp_bonus[3]  += 10;  // mouvement silencieux
//       comp_bonus[8]  += 10;  // évasion
//       comp_bonus[9]  += 10;  // escalade
//       comp_bonus[12] += 5;   // creuser des tunnels
//       comp_bonus[13] += 10;  // se cacher dans l'ombre
//       comp_bonus[16] += 5;   // trouver et désamorcer les pièges
//       comp_bonus[17] += 10;  // vol à la tire
//       break;
//     case 19:
//       comp_bonus[2]  += 20;  // crochetage
//       comp_bonus[3]  += 15;  // mouvement silencieux
//       comp_bonus[8]  += 15;  // évasion
//       comp_bonus[9]  += 15;  // escalade
//       comp_bonus[12] += 10;  // creuser des tunnels
//       comp_bonus[13] += 15;  // se cacher dans l'ombre
//       comp_bonus[16] += 10;  // trouver et désamorcer les pièges
//       comp_bonus[17] += 15;  // vol à la tire
//       break;
//     case 20:
//       comp_bonus[2]  += 20;  // crochetage
//       comp_bonus[3]  += 15;  // mouvement silencieux
//       comp_bonus[8]  += 20;  // évasion
//       comp_bonus[9]  += 20;  // escalade
//       comp_bonus[12] += 15;  // creuser des tunnels
//       comp_bonus[13] += 20;  // se cacher dans l'ombre
//       comp_bonus[16] += 15;  // trouver et désamorcer les pièges
//       comp_bonus[17] += 20;  // vol à la tire
//       break;
//     case 21:
//       comp_bonus[2]  += 25;  // crochetage
//       comp_bonus[3]  += 20;  // mouvement silencieux
//       comp_bonus[8]  += 25;  // évasion
//       comp_bonus[9]  += 20;  // escalade
//       comp_bonus[12] += 20;  // creuser des tunnels
//       comp_bonus[13] += 25;  // se cacher dans l'ombre
//       comp_bonus[16] += 20;  // trouver et désamorcer les pièges
//       comp_bonus[17] += 20;  // vol à la tire
//       break;
//     case 22:
//       comp_bonus[2]  += 25;  // crochetage
//       comp_bonus[3]  += 20;  // mouvement silencieux
//       comp_bonus[8]  += 30;  // évasion
//       comp_bonus[9]  += 25;  // escalade
//       comp_bonus[12] += 30;  // creuser des tunnels
//       comp_bonus[13] += 30;  // se cacher dans l'ombre
//       comp_bonus[16] += 20;  // trouver et désamorcer les pièges
//       comp_bonus[17] += 25;  // vol à la tire
//       break;
//     }

  map< unsigned, unsigned > correspondance = map_list_of
    ( 1,  0)
    ( 2,  1)
    ( 3,  2)
    ( 4,  3)
    ( 5,  4)
    ( 6,  5)
    ( 8,  6)
    ( 9,  7)
    (11,  8)
    (12,  9)
    (13, 10)
    (16, 11)
    (17, 12);

  unsigned combien_comp(Competences.size()), valeur;
  for (unsigned i(0); i< combien_comp; i++)
    {
      valeur = Competences[i];
      if (Comp[valeur].pourcentage())
	{
	  Competences[i + 1] += comp_bonus -> at( correspondance[valeur] );
	  i++;
	}
    }

  /*Debug::*/depileNiveau();
}

/**
 * Ici on peut augmenter les talents liés à un pourcentage à chaque
 * passage de niveau et à la création.
 *
 * Une sauvegarde de l'état courant est faite avant de modifier quoi
 * que ce soit, et ensuite on afiche un récapitulatif des changements,
 * qui doivent être validés.
 */
void Voleur::augmenterTalents( const string& /*nomRace*/, 
			       const vector< unsigned >& /*traits*/,
			       const bool& lvl_up )
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Voleur::augmenterTalents -> début" << endmsg;
  // 30 pts si augmente niveau
  vector<unsigned> quelle, quelleValeur;
  unsigned taille( tailleCompetences() ), nbr( 0 ), depense, laquelle;

  unsigned bonus( getPourcent( lvl_up ) );

  for (unsigned i(0); i<taille; i++)
    {
      debug() << Competences[i];
      if (Comp[Competences[i]].pourcentage())
	{
	  debug() << " -> oui";
	  quelle.push_back(Competences[i]);
	  quelleValeur.push_back(Competences[i+1]);
	  i++;
	  nbr++;
	}
      debug() << endmsg;
    }

  unsigned pps(bonus);
  if (lvl_up)
    pps = 30;
  blank() << "Tu as " << pps << " point à dépenser dans les compétences";
  blank() <<" suivantes:" << endmsg;
  for (unsigned i(0); i<nbr; i++)
    {
      debug() << quelle[i] << " => " << Comp[quelle[i]].nom() << endmsg;
      blank() << i+1 << " " << Comp[quelle[i]].nom() << " " << quelleValeur[i] 
		  << "%" << endmsg;
    }

  string poubelle;

  do
    {
      laquelle =
	demanderUnsignedBorne("Quelle compétence veux-tu améliorer? ", 1,
				nbr + 1);

      depense = demanderUnsignedBorne("De combien? ", 0, pps);

      quelleValeur[laquelle-1] += depense;
      pps -= depense;
    }
  while(pps!=0);

  vector<unsigned> tmp;
  unsigned j(0);

  // On va netoyer tt ça, pas besoin de faire une copie... ou alors demander
  // confirmation!
  for (unsigned i(0); i<taille; i++)
    {
      if (Comp[Competences[i]].pourcentage())
	{
	  tmp.push_back(Competences[i]);
	  tmp.push_back(quelleValeur[j]);
	  j++;
	  i++;
	}
      else
	tmp.push_back(Competences[i]);
    }
  Competences.clear();
   for (unsigned i(0); i<taille; i++)
     Competences.push_back(tmp[i]);
  
  func() << "Voleur::augmenterTalents -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

void Voleur::js(unsigned *tab) const
{
  *(tab + 0) = 13; // Paralysie, Poison ou Mort magique
  *(tab + 1) = 14; // Bâton, batonnet baguette
  *(tab + 2) = 12; // Pétrification ou Métamorphose
  *(tab + 3) = 16; // Souffles
  *(tab + 4) = 15; // Sorts
  
  unsigned cat;
  if (Niveau <= 4)
    cat = 0;
  else  if (Niveau <= 8)
    cat = 1;
  else  if (Niveau <= 12)
    cat = 2;
  else  if (Niveau <= 16)
    cat = 3;
  else  if (Niveau <= 20)
    cat = 4;
  else
    cat = 5;

  *(tab + 0) -= cat;
  *(tab + 1) -= 2 * cat;
  *(tab + 2) -= cat;
  *(tab + 3) -= cat;
  *(tab + 4) -= 2 * cat;
}

unsigned Voleur::calculeTAc0() const
{
  unsigned mod_niveau((Niveau - 1) / 2);

  return 20 - mod_niveau;
}

void Voleur::calculNivMag()
{
  NiveauMagique = 0;
}

/**
 * Permet de gérer les compétences martiales, càd lesquelles sont accessibles
 * à quel coût.
 *
 * Un Voleur peut prendre un arme réservée au Voleur pour 3 pp, ou alors une
 * arme de Guerrier pour 4 pp.
 */
void Voleur::prixAcces(map<string, unsigned>*& Map) const
{
  /*Debug::*/empileNiveau( &fonction );
  //DebugLevelType etat_precedent(dout.getLevel());
  //dout.setLevel(fonction);
  func() << "Voleur::prixAcces -> début" << endmsg;

  map<string, unsigned>::const_iterator mapIt, mapBegin(Map -> begin()),
    mapEnd(Map -> end());

  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
    lfunc() << mapIt -> first << endmsg;

  
  mapIt = Map -> find("Voleur");
  if ((mapIt != mapEnd && mapIt -> second > 3) || mapIt == mapEnd)
    Map -> operator[]("Voleur")   = 3;
    mapIt = Map -> find("Guerrier");
    if ((mapIt != mapEnd && mapIt -> second > 4) || mapIt == mapEnd)
    Map -> operator[]("Guerrier") = 4;

  func() << "Voleur::prixAcces -> fin" << endmsg;
  /*Debug::*/depileNiveau();//dout.setLevel(etat_precedent);
}


unsigned Voleur::getPourcent(const bool& lvlUp) const
{
  if ( lvlUp )
    return 30;
  else
    return 60;
}

string Voleur::getScoreAdditionnel(const unsigned& i) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Voleur::getScoreAdditionnel -> début" << endmsg;

  string resultat( "" );

  if ( Comp[ i ].nom() == "Attaque sournoise" )
    {
      switch( Niveau )
	{
	case 1:
	case 2:
	case 3:
	case 4:
	  resultat = "dégâts x2";
	  break;
	case 5:
	case 6:
	case 7:
	case 8:
	  resultat = "dégâts x3";
	  break;
	case 9:
	case 10:
	case 11:
	case 12:
	  resultat = "dégâts x4";
	  break;
	default:
	  resultat = "dégâts x5";
	}
    }
  else if ( Comp[ i ].nom() == "Utilisation de parchemins (niv 10)" ||
	    Comp[ i ].nom() == "Utilisation de parchemins" )
    {
      switch( Niveau )
	{
	case 1:
	case 2:
	  resultat = "10";
	  break;
	case 3:
	case 4:
	  resultat = "20";
	  break;
	case 5:
	case 6:
	  resultat = "30";
	  break;
	case 7:
	  resultat = "40";
	  break;
	case 8:
	  resultat = "50";
	  break;
	case 9:
	  resultat = "60";
	  break;
	case 10:
	  resultat = "70";
	  break;
	default:
	  resultat = "80";
	}
    }

  func() << "Voleur::getScoreAdditionnel -> fin" << endmsg;
  /*Debug::*/depileNiveau();

  return resultat;
}

unsigned Voleur::aSpecialisation() const
{
  return find( Competences.begin(), Competences.end(), 20 ) !=
    Competences.end() ? 1 : 0;
}
